#include "charge.h"
#include "charge_protocol.h"
#include "time.h"

static uint16_t _recv_seq = 0x00, resp_ack = 0x00;

extern void price_set(charger_t *charger, price_t *p);

extern ql_para_form_main  gt_para_from_main;

int8_t proto_get_recv_cmd_index(uint8_t cmd);


void proto_uint32_to_bytes_little(uint8_t *data, uint32_t num)
{
    int i = 0;

    for (i = 0; i < sizeof(uint32_t); i++)
    {
        data[i] = (uint8_t)((num & (0xFF << i * 8)) >> (8 * i));
    }
}

void proto_uint16_to_bytes_little(uint8_t *data, uint16_t num)
{
    int i = 0;

    for (i = 0; i < sizeof(uint16_t); i++)
    {
        data[i] = (uint8_t)((num & (0xFF << i * 8)) >> (8 * i));
    }
}

void proto_hex_dump(int8_t *name, uint8_t *buf, int32_t len)
{
#if 1 // IS_DEBUG_HEX
    int32_t i = 0;
    log_printf("hex:######### %s:%d\r\n", name, len);
    for (i = 0; i < len; i ++)
    {
        if (0 == (i % 16))
        {
            log_printf("\r\n");
        }

        log_printf("%02x ", buf[i]);
    }
    log_printf("\r\n\r\n=========================================\r\n");
#endif
}

static uint16_t proto_crc16(uint8_t *data, uint32_t datalen, uint16_t pre_crc)
{
    static uint16_t CRCTalbeAbs[] =
    {
        0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
        0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400,
    };

    uint16_t wCRC = pre_crc;
    uint32_t i = 0;
    uint8_t chChar;

    for (i = 0; i < datalen; i++)
    {
        chChar = *data++;
        wCRC = CRCTalbeAbs[(chChar ^ wCRC) & 15] ^ (wCRC >> 4);
        wCRC = CRCTalbeAbs[((chChar >> 4) ^ wCRC) & 15] ^ (wCRC >> 4);
    }

    return wCRC;
}

void crc16_test(void)
{
#if 0
    uint16_t default_crc = 0xFFFF;
    char data[] = {0x18, 0xff, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x20, \
            0x00, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x50, 0x49, \
            0x4c, 0x45, 0x5f, 0x30, 0x30, 0x30, 0x30, 0x30, \
            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, \
            0x30, 0x30, 0x30, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00
        };

    printf("crc:%04x\r\n", proto_crc16(data, sizeof(data)), default_crc);
#endif
}

uint8_t proto_get_gun_id_by_pack(net_client_t *client, uint8_t *buf, uint32_t len)
{
    uint32_t len_plain = 0, len_crypt = 0;
    uint8_t *p_plain = NULL, *p_crypt = NULL;
    uint8_t gun_id = 0;
#define SIZE_OF_PACKET_22 48

    if (buf == NULL || len != SIZE_OF_PACKET_22)
    {
        log_error("wrong bill data:%d\r\n", len);
        return 0;
    }
    p_plain = buf;
    len_plain = len;

#if SUPPORT_CRYPT
    memset(client->crypt_buf, 0, sizeof(client->crypt_buf));
    p_crypt = buf;
    len_crypt = len;
    len_plain = sizeof(client->crypt_buf);
    if (client->decrypt(p_crypt, len_crypt, client->crypt_buf, &len_plain, client->crypt_key) < 0)
    {
        log_error("decrypt data err\r\n");
        return 0;
    }
    else
    {
        p_plain = client->crypt_buf;
    }
#endif

    gun_id = p_plain[PILE_ID_MAX_LEN];

    log_debug("got gun id [%d] by pack 22!\r\n", gun_id);

    return gun_id;
}

static uint16_t proto_next_seq(void)
{
    static uint16_t _send_seq = 0x00;
    return _send_seq++;
}
void proto_set_ack(uint16_t seq)
{
    resp_ack = seq;
}
static uint16_t proto_get_ack(void)
{
    return resp_ack;
}

//endcode header & crc and encrypt data
int32_t proto_encode_pack(net_client_t *client, uint8_t *data, uint16_t data_len, uint8_t cmd)
{
    uint32_t encrypt_len = 0;
    int32_t need_len = 0;
    int32_t offset = 0;
    uint8_t *p_seq_location = NULL, *p_len_location = NULL;
    uint16_t crc = 0, seq = 0, ack = 0, default_crc = 0xFFFF;
    uint16_t temp = 0;
    net_queue_t *q = &client->snd_queue;

    if (0 == data_len)
    {
        return -1;
    }

    encrypt_len = (data_len + 4) + (16 - ((data_len + 4) % 16));
    need_len = PROTO_MIN_LEN + encrypt_len;

    if (need_len > q->free_len)
    {
        log_error("q->free_len not enough need_len =%d q->free_len =%d, cmd:%02x\r\n", need_len, q->free_len, cmd);
        return ERR_PROTOCOL_BUFFER_ERROR;
    }

    /*     encode magic         */
    temp = PROTO_MAGIC;
    memcpy(&q->curr[offset], &temp, 2);
    offset += 2;
    /*     encode verison       */
    temp = PROTO_VERSION;
    memcpy(&q->curr[offset], &temp, 2);
    offset += 2;
    /*     encode cmd        */
    q->curr[offset++] = cmd;
    /*     ignore length       */
    p_len_location = &q->curr[offset];
    offset += 2;
    /*     encode seq        */
    seq = proto_next_seq();
    memcpy(&q->curr[offset], &seq, 2);
    p_seq_location = &q->curr[offset];
    offset += 2;
    /*     encode ack        */
    ack = proto_get_ack();
    memcpy(&q->curr[offset], &ack, 2);
    offset += 2;
    /*     encode data         */
    memcpy(&q->curr[offset], data, data_len);
    /*     encrypt seq & ack & data         */
#if SUPPORT_CRYPT
    if (cmd == CMD_F0)
    {
        offset += data_len;
        data_len += 4;//2(seq)+2(ack)
    }
    else
    {
        memset(client->crypt_buf, 0, sizeof(client->crypt_buf));
        client->encrypt(p_seq_location, data_len + 4, client->crypt_buf, &encrypt_len, client->crypt_key);
        data_len = encrypt_len;
        offset = offset - 4 + data_len;
        memcpy(p_seq_location, client->crypt_buf, encrypt_len);
    }
#else
    offset += data_len;
    data_len += 4;//2(seq)+2(ack)
#endif
    /*     encode data len       */
    memcpy(p_len_location, &data_len, 2);
    /*     encode crc16         */
    crc = proto_crc16(q->curr, offset, default_crc);
    memcpy(&q->curr[offset], &crc, 2);
    offset += 2;

#if IS_DEBUG_HEX
    printf("\r\n>>>>>>>>>>>>>>>> EN QUEUE >>>>>>>>>>>>>>>>>\r\n\r\nseq:[ %d ], cmd:[ %02x ], total len:%d====\r\n", seq, cmd, offset);
    proto_hex_dump("", q->curr, offset);
#endif

    if (net_queue_en(q, offset, cmd, seq) < 0)
    {
        return ERR_PROTOCOL_BUFFER_ERROR;
    }

    return offset;
}

int32_t proto_decode_pack(uint8_t *data, uint16_t len, charge_protocol *proto, int8_t *cmd_idx)
{
    uint16_t calc_crc = 0, default_crc = 0xFFFF;
    int32_t offset = 0;

    if (data == NULL)
    {
        return ERR_PROTOCOL_NULL_POINTER;
    }

    if (len == 0)
    {
        return 0;
    }
    /*     decode magic         */
    memcpy((uint8_t *) & (proto->magic), &data[offset], sizeof(proto->magic));
    offset += sizeof(proto->magic);
    if (proto->magic != PROTO_MAGIC)
    {
        log_error("proto magic err:%04x\r\n", proto->magic);
        return ERR_PROTOCOL_MAGIC_INVALID;
    }
    /*     decode version         */
    memcpy((uint8_t *) & (proto->version), &data[offset], sizeof(proto->version));
    offset += sizeof(proto->version);
    if (proto->version != PROTO_VERSION)
    {
        log_error("proto version err:%04x\r\n", proto->version);
        return ERR_PROTOCOL_VERSION_INVALID;
    }
    /*     decode cmd         */
    memcpy((uint8_t *) & (proto->cmd), &data[offset], sizeof(proto->cmd));
    offset += sizeof(proto->cmd);
    /*     decode length      */
    memcpy((uint8_t *) & (proto->length), &data[offset], sizeof(proto->length));
    offset += sizeof(proto->length);
    if (proto->length > (len - offset - PROTO_CRC_LEN))
    {
        //need recv more data
        log_error("protocol len not enough, need_len:%d - recv:%d\r\n", proto->length, len - offset - PROTO_CRC_LEN);
        return 0;
    }
    log_debug("proto->length:%d\r\n", proto->length);
    /*  the data include region (seq + ack + data)  */
    proto->data = &data[offset];
    /*     verify crc         */
    calc_crc = proto_crc16(data, proto->length + PROTO_SEQ_OFFSET, default_crc);
    memcpy((uint8_t *) & (proto->crc), &data[offset + proto->length], sizeof(proto->crc));

    if (proto->crc != calc_crc)
    {
        log_error("protocol crc err: recv crc:%d, calc_crc:%d\r\n", proto->crc, calc_crc);
        return ERR_PROTOCOL_CRC_INVALID;
    }
    /*     verify cmd         */
    *cmd_idx = proto_get_recv_cmd_index(proto->cmd);
    if (*cmd_idx < 0)
    {
        log_error("proto cmd[%d] not support\r\n", proto->cmd);
        return ERR_PROTOCOL_CMD_INVALID;
    }

    return (proto->length + PROTO_MIN_LEN);
}

static uint32_t proto_decode_cp56time2a(uint8_t *data, int32_t len)
{
    cp56time2a *t = NULL;
    struct os_time ot = {0};

    if (7 != len || NULL == data)
    {
        return 0;
    }

    t = (cp56time2a *)data;
    log_debug("%04d-%02d-%02d: %02d:%02d:%02d %u\n", \
              t->year+2000, t->mon, t->mday, t->hour, t->min, t->msec/1000, t->msec%1000);
    ot.year = t->year + 2000;
    ot.mon = t->mon;
    ot.day = t->mday;
    ot.hour = t->hour;
    ot.min = t->min;
    ot.sec = t->msec / 1000;
    return os_time_to_ts(ot);
}

static int32_t proto_encode_cp56time2a(uint8_t *data, int32_t len, time_t ts)
{
    cp56time2a *t = NULL;
    struct os_time ot = {0};

    if (len != 7 || NULL == data)
    {
        return -1;
    }

    os_ts_to_time(ts, &ot);

    t = (cp56time2a *)data;

    t->year = ot.year - 2000;
    t->mon = ot.mon;
    t->mday = ot.day;
    t->hour = ot.hour;
    t->min = ot.min;
    t->msec = ot.sec * 1000;

    t->res1 = 0;
    t->iv = 0;
    t->res2 = 0;
    t->res3 = 0;
    t->res4 = 0;
    t->wday = 0;

    //log_debug("ts2cp56time: %02x%02x%02x%02x%02x%02x%02x\n", \
    //    (uint8_t)data[0], (uint8_t)data[1], (uint8_t)data[2], \
    //  (uint8_t)data[3], (uint8_t)data[4], (uint8_t)data[5], (uint8_t)data[6]);
    log_debug("ts2cp56time: %4d %d %d %d %d %d \n", \
    		t->year, t->mon, t->mday, t->hour, t->min, t->msec);

    return 0;
}

static int proto_encode_access_sign(uint8_t *rand_str, uint8_t *access_token, uint8_t *access_sign)
{
    unsigned char buf[32];

    if (NULL == access_token || NULL == access_sign)
    {
        return -1;
    }

    memcpy(buf, rand_str, 6);
    memcpy(&buf[6], access_token, 16);

    encode_md5(access_sign, 32, buf, 22);

    return 0;
}

/***********************************************************************************************
                                (三) 连接基础数据交互定义
***********************************************************************************************/
//充电桩接入认证请求
int32_t proto_encode_f0(void *p, uint8_t *data, uint16_t data_len)
{
    int32_t offset = 0, i = 0;
    uint8_t random_str[6] = {0};

    if (data == NULL)
    {
        return -1;
    }

    for (i = 0; i < 6; i++)
    {
        //ASCII可见字符范围是[32,126]
        random_str[i] = (uint8_t)(os_random() % 95 + 32);
    }
    log_debug("↑↑↑↑发送接入f0\r\n");
    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    memcpy(&data[offset], random_str, 6);
    offset += sizeof(random_str);

    memcpy(&data[offset], CHARGE_ACCESS_ID, 9);
    offset += 9;

    proto_encode_access_sign(random_str, (unsigned char *)CHARGE_ACCESS_TOKEN, &data[offset]);
    offset += 32;

    data[offset++] = SUPPORT_CRYPT;

    return offset;
}

//云服务器接入认证应答
int32_t proto_decode_f1(void *c, uint8_t *data, uint16_t data_len)
{
    cmd_f1_t cmd;
    int32_t offset = 0;
    charger_t *charger = (charger_t *)c;
    uint32_t out_len = 0;
    unsigned char buf[32], expect_sign[32];

    if (data == NULL)
    {
        return -1;
    }

    memset(&cmd, 0x00, sizeof(cmd));
    memcpy(&(cmd.pile_id), &data[offset], PILE_ID_MAX_LEN);
    offset += PILE_ID_MAX_LEN;

    cmd.access_status = data[offset++];
    memcpy(&(cmd.random_str), &data[offset], 6);
    offset += 6;

    memcpy(&(cmd.sign), &data[offset], 32);
    offset += 32;

    memset(buf, 0x00, sizeof(buf));
    memset(expect_sign, 0x00, sizeof(expect_sign));

    buf[0] = cmd.access_status;
    memcpy(&buf[1], cmd.random_str, 6);
    memcpy(&buf[7], (unsigned char *)CHARGE_ACCESS_TOKEN, 16);

    encode_md5(expect_sign, 32, buf, 23);
    if (memcmp(expect_sign, cmd.sign, 32) != 0)
    {
        log_error("↓↓↓↓收到接入f1:验签失败,收到签名:%s,期望签名:%s\r\n", cmd.sign, expect_sign);
        goto fatal_err;
    }

    if (cmd.access_status == '0')
    {
#if SUPPORT_CRYPT
        out_len = sizeof(charger->net_client.crypt_key);
        if (charger->net_client.decrypt(&data[offset], 32, charger->net_client.crypt_key, &out_len, (unsigned char *)CHARGE_ACCESS_TOKEN) != 0)
        {
            log_error("decrypt key err!\r\n");
            return -1;
        }
        charger->net_client.crypt_key[out_len] = '\0';
        log_debug("采用加密模式，密钥:%s\r\n", charger->net_client.crypt_key);
#endif
        charger->net_client.net_status = NET_STATUS_ACCESS_ALLOW;
        event_add_common(&charger->event, EVENT_TYPE_CONNECT, 0, NULL, 0);

        log_debug("↓↓↓↓收到接入f1，接入成功!\r\n");
    }
    else if (cmd.access_status == '1')
    {
        log_error("↓↓↓↓收到接入f1:失败，1: 签名错误\r\n");
        goto fatal_err;
    }
    else if (cmd.access_status == '2')
    {
        log_error("↓↓↓↓收到接入f1:失败，2: 桩编号[ %s ]已在线\r\n", CHARGE_PILE_ID);
        goto fatal_err;
    }
    else if (cmd.access_status == '3')
    {
        log_error("↓↓↓↓收到接入f1:失败，3: 接入ID不存在\r\n");
        goto fatal_err;
    }
    else if (cmd.access_status == '4')
    {
        log_error("↓↓↓↓收到接入f1:失败，4: 桩编号在黑名单中\r\n");
        goto fatal_err;
    }
    else
    {
        log_error("↓↓↓↓收到接入f1:失败，未知错误:%d\r\n", cmd.access_status);
        goto fatal_err;
    }
    return 0;

fatal_err:
    os_sleep_ms(10000);

    return -1;
}

//充电桩上报心跳数据
int32_t proto_encode_f2(void *p, uint8_t *data, uint16_t data_len)
{
    int32_t offset = 0;

    log_debug("↑↑↑↑发送心跳f2\r\n");

    if (data == NULL)
    {
        return -1;
    }

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    return offset;
}

//云服务器心跳应答
int32_t proto_decode_f3(void *c, uint8_t *data, uint16_t data_len)
{
    cmd_00_t cmd;
    int32_t offset = 0;

    if (data == NULL)
    {
        return -1;
    }

    memset(&cmd, 0x00, sizeof(cmd));
    memcpy(&(cmd.pile_id), &data[offset], PILE_ID_MAX_LEN);

    log_debug("↓↓↓↓收到心跳回复f3:pile_id:%s\r\n", cmd.pile_id);
    return 0;
}

//充电桩上报版本信息
int32_t proto_encode_f4(void *c, uint8_t *data, uint16_t data_len)
{
    uint32_t offset = 0x00;
    uint8_t string[8] = {0x00,};
    char *p;

    if (data == NULL)
    {
        return -1;
    }

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    memcpy(string, CHARGE_SOFTWARE_VERSION, strlen(CHARGE_SOFTWARE_VERSION));

    p = strtok(string, ".");
    while (p != NULL)
    {
        data[offset++] = (uint8_t)(atoi(p) & 0x000000FF);
        p = strtok(NULL, ".");
    }

    log_debug("↑↑↑↑充电桩上报版本信息f4\r\n");

    return offset;
}

//云服务器下发获取版本指令
int32_t proto_decode_f5(void *c, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)c;

    if (data == NULL)
    {
        return -1;
    }

    event_add_common(&charger->event, EVENT_TYPE_SEND_VERSION, 0, NULL, _recv_seq);

    log_debug("↓↓↓↓收到云服务器下发获取版本指令\r\n");
    return 0;
}

int32_t proto_decode_00(void *c, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)c;
    cmd_00_t cmd;
    int32_t offset = 0;

    if (data == NULL)
    {
        return -1;
    }

    memset(&cmd, 0x00, sizeof(cmd));
    memcpy(&(cmd.pile_id), &data[offset], PILE_ID_MAX_LEN);

    log_debug("↓↓↓↓收到获取电气参数 00:id:%s\r\n", cmd.pile_id);

    event_add_common(&charger->event, EVENT_TYPE_GET_INFO, 0, NULL, _recv_seq);

    return 0;
}

int32_t proto_encode_01(void *p, uint8_t *data, uint16_t data_len)
{
    user_pile_t *pile_info = (user_pile_t *)p;

    int32_t offset = 0;

    log_debug("↑↑↑↑发送电气参数01\r\n");

    if (data == NULL)
    {
        return -1;
    }

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = CHARGE_GUN_CNT;
    data[offset++] = CHARGE_PILE_TYPE;
    memcpy(&data[offset], &pile_info->rated_voltage, sizeof(pile_info->rated_voltage));
    offset += sizeof(pile_info->rated_voltage);
    memcpy(&data[offset], &pile_info->rated_current, sizeof(pile_info->rated_current));
    offset += sizeof(pile_info->rated_current);
    memcpy(&data[offset], &pile_info->max_voltage, sizeof(pile_info->max_voltage));
    offset += sizeof(pile_info->max_voltage);
    memcpy(&data[offset], &pile_info->max_current, sizeof(pile_info->max_current));
    offset += sizeof(pile_info->max_current);
    memcpy(&data[offset], &pile_info->max_power, sizeof(pile_info->max_power));
    offset += sizeof(pile_info->max_power);
    memcpy(&data[offset], &pile_info->min_power, sizeof(pile_info->min_power));
    offset += sizeof(pile_info->min_power);
    memcpy(&data[offset], &pile_info->rated_power, sizeof(pile_info->rated_power));
    offset += sizeof(pile_info->rated_power);

//    log_debug("%d %d %d %d %d %d %d\r\n", pile_info->rated_voltage, pile_info->rated_current,
    //      pile_info->max_voltage, pile_info->max_current, pile_info->max_power, pile_info->min_power,
    //    pile_info->rated_power);

    return offset;
}

/*充电桩上报环境数据*/
int32_t proto_encode_02(void *p, uint8_t *data, uint16_t data_len)
{
    user_pile_t *pile_info = (user_pile_t *)p;
    int32_t offset = 0;

    if (data == NULL || data_len <= PILE_ID_MAX_LEN + 2)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送上报环境数据02\r\n");

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = pile_info->temperature;
    data[offset++] = pile_info->humidity;

    return offset;
}

/*  下发重启指令*/
int32_t proto_decode_03(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    int32_t offset = 0;

    if (data == NULL || p == NULL)
    {
        return -1;
    }

    offset += PILE_ID_MAX_LEN;
    charger->pile_cmd = data[offset++];

    event_add_common(&charger->event, EVENT_TYPE_REBOOT, 0, &charger->pile_cmd, _recv_seq);

    log_debug("↓↓↓↓收到重启指令03 : %d\r\n", charger->pile_cmd);

    return 0;
}

/*充电桩应答 重启指令*/
int32_t proto_encode_04(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    int32_t offset = 0;

    if (charger == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩应答 重启指令04\r\n");

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    if (charger->error_code != 0)
    {
        data[offset++] = 0x01;
    }
    else
    {
        data[offset++] = 0x00;
    }

    return offset;
}
/*  下发功率分配策略指令*/
int32_t proto_decode_05(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    gun_t *gun = NULL;
    uint8_t gun_index = 0, gun_id = 0;

    int32_t offset = 0;

    if (charger == NULL || data == NULL || data_len != CMD_05_LEN)
    {
        return -1;
    }

    gun_id = data[PILE_ID_MAX_LEN];
    gun_index = gun_id - 1;
    gun = (gun_t *) & (charger->gun[gun_index]);
    memcpy(&(gun->max_out_power), data + 33, 4);

    event_add_common(&charger->event, EVENT_TYPE_POWER_ALLOC, gun_id, NULL, _recv_seq);

    log_debug("↓↓↓↓收到功率分配策略指令05 \r\n");

    return 0;
}

/*充电桩应答 功率分配策略指令*/
int32_t proto_encode_06(void *p, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)p;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }
    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = gun->gun_id;

    if (gun->gun_fault_code != 0)
    {
        data[offset++] = 0x01;
    }
    else
    {
        data[offset++] = 0x00;
    }
    log_debug("↑↑↑↑发送充电桩应答 功率分配策略指令06\r\n");

    return offset;
}

/*  下发设置充电桩工作参数指令*/
int32_t proto_decode_07(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    int32_t offset = 0;

    if (data == NULL || p == NULL)
    {
        return -1;
    }

    log_debug("↓↓↓↓收到设置充电桩工作参数指令 07 \r\n");

    offset += PILE_ID_MAX_LEN;
    memcpy(&(charger->local_cfg.param.period_chargeing), &data[offset], 2);
    offset += 2;
    memcpy(&(charger->local_cfg.param.period_heartbeat), &data[offset], 2);
    offset += 2;
    memcpy(&(charger->local_cfg.param.heartbeat_timeout_cnt), &data[offset], 2);
    offset += 2;
    charger->local_cfg.param.log_lvl = data[offset++];
    charger->local_cfg.param.log_up_strategy = data[offset++];
    memcpy(&(charger->local_cfg.param.log_period), &data[offset], 2);
    offset += 2;

    log_debug("上报间隔:%d, 心跳间隔:%d, 心跳次数:%d, 日志level:%d, 日志策略:%d, 日志间隔:%d\r\n",
        charger->local_cfg.param.period_chargeing, charger->local_cfg.param.period_heartbeat, charger->local_cfg.param.heartbeat_timeout_cnt,
        charger->local_cfg.param.log_lvl, charger->local_cfg.param.log_up_strategy, charger->local_cfg.param.log_period);

    memcpy(&(charger->pile->param_group), &data[offset], 56);

    printf("sizeof charger->pile->param_group:%ld\r\n", sizeof(charger->pile->param_group));

    event_add_common(&charger->event, EVENT_TYPE_SET_PARAMS, 0, NULL, _recv_seq);

    return 0;
}

/*充电桩应答工作参数设置指令*/
int32_t proto_encode_08(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    int32_t offset = 0;

    if (charger == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩应答 应答工作参数设置指令08\r\n");
    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    if (charger->error_code != 0)
    {
        data[offset++] = 0x01;
    }
    else
    {
        data[offset++] = 0x00;
    }
    return offset;
}

/*充电阶段上报实时数据*/
int32_t proto_encode_21(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    fee_t *fee = (fee_t *) & (gun->fee);
    user_gun_t *u_gun = gun->u_gun;
    user_bms_t *u_bms = gun->bms;
    int32_t offset = 0;

    if (gun == NULL || data == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送实时数据0x21, gun id:%d, state:%d\r\n", gun->gun_id, gun->gun_state);
	log_debug("↑↑↑↑Volt:%d, Current:%d, time:%d, KWH:%d\r\n", u_gun->voltage, u_gun->current, fee->total_kwh);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;

    data[offset++] = gun->gun_state;
    data[offset++] = u_gun->temperature;
    data[offset++] = gun->start_mode;

    memcpy(&data[offset], &(u_gun->voltage), sizeof(u_gun->voltage));
    offset += sizeof(u_gun->voltage);

    memcpy(&data[offset], &u_gun->current, sizeof(u_gun->current));
    offset += sizeof(u_gun->current);

    data[offset++] = u_gun->battery_max_temp;
    data[offset++] = u_gun->battery_min_temp;
    data[offset++] = u_gun->soc;

    memcpy(&data[offset], &fee->total_time, sizeof(fee->total_time));
    offset += sizeof(fee->total_time);

    memcpy(&data[offset], &u_gun->remain_charge_time, sizeof(u_gun->remain_charge_time));
    offset += sizeof(u_gun->remain_charge_time);

    memcpy(&data[offset], &fee->total_kwh, sizeof(fee->total_kwh));
    offset += sizeof(fee->total_kwh);

    memcpy(&data[offset], &u_gun->lose_charge_kwh, sizeof(u_gun->lose_charge_kwh));
    offset += sizeof(u_gun->lose_charge_kwh);

    uint32_t total_fee_elec = (fee->total_fee_elec + 5000) / 10000;
    memcpy(&data[offset], &total_fee_elec, sizeof(total_fee_elec));
    offset += sizeof(total_fee_elec);

    uint32_t total_fee_service = (fee->total_fee_service + 5000) / 10000;
    memcpy(&data[offset], &total_fee_service, sizeof(total_fee_service));
    offset += sizeof(total_fee_service);

    memcpy(&data[offset], gun->card_id, CARD_ID_LEN);
    offset += CARD_ID_LEN;

    memcpy(&data[offset], u_bms->brm_vin, BRM_VIN_LEN);
    offset += BRM_VIN_LEN;

    return offset;
}

/*充电桩上报刷卡启动充电*/
int32_t proto_encode_22(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送刷卡启动充电0x22,枪号:%d\r\n", gun->gun_id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;

    memcpy(&data[offset], gun->card_id, CARD_ID_LEN);
    offset += CARD_ID_LEN;

    return offset;
}

/*云服务器应答刷卡启动*/
int32_t proto_decode_23(void *param, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)param;
    account_t *account = NULL;
    gun_t *gun = NULL;
    uint8_t gun_id = 0, gun_index = 0, state = 0;

    if (data == NULL)
    {
        return -1;
    }

    gun_id = data[PILE_ID_MAX_LEN];
    gun_index = gun_id - 1;

    account = (account_t *) & (charger->gun[gun_index].account);
    gun = (gun_t *) & (charger->gun[gun_index]);

    memcpy(&(account->balance), &data[33], 4);
    account->type = TYPE_MONEY;
    account->value = account->balance;

    gun->start_mode = START_MODE_CARD;

    state = data[37];

    if (state == 0)
    {
        event_add_common(&charger->event, EVENT_TYPE_CHARGE_START, gun_id, NULL, 0);
    }
    gun->card_state = CARD_STATE_UNLOCK;
    log_debug("↓↓↓↓收到云服务器应答刷卡启动0x23, gun_id:%d, balance:%d, state:%d\r\n", gun_id, account->balance, state);

    return 0;
}

/*充电桩主动上报结束充电*/
int32_t proto_encode_24(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩主动上报结束充电 0x24,枪号:%d\r\n", gun->gun_id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;

    data[offset++] = U16_LO(gun->fee.stop_code);
    data[offset++] = U16_HI(gun->fee.stop_code); //20210619 add by cz

    return offset;
}

/*服务器应答充电结束*/
int32_t proto_decode_25(void *param, uint8_t *data, uint16_t data_len)
{
    int32_t offset = 0;
    cmd_25_t cmd;

    if (data == NULL)
    {
        return -1;
    }

    memset(&cmd, 0x00, sizeof(cmd));

    memcpy(&(cmd.pile_id), &data[offset], PILE_ID_MAX_LEN);
    offset += PILE_ID_MAX_LEN;
    cmd.gun_id = data[offset++];
    cmd.rsp_state = data[offset++];

    log_debug("↓↓↓↓收到服务器应答充电结束 0x25 id:%s, gun_id:%d, state:%d\r\n", cmd.pile_id, cmd.gun_id, cmd.rsp_state);

    return offset;
}

/* APP 下发启动充电指令*/
int32_t proto_decode_26(void *param, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)param;
    account_t *account = NULL;
    gun_t *gun = NULL;
    uint8_t gun_index = 0, gun_id = 0;

    if (charger == NULL || data == NULL || data_len != 42)
    {
        return -1;
    }

    gun_id = data[PILE_ID_MAX_LEN];
    gun_index = gun_id - 1;

    account = (account_t *) & (charger->gun[gun_index].account);
    gun = (gun_t *) & (charger->gun[gun_index]);

    memcpy(&(account->balance), data + 33, 4);
    account->type = (balance_type)data[37];
    memcpy(&(account->value), data + 38, 4);

    gun->start_mode = START_MODE_APP;

    event_add_common(&charger->event, EVENT_TYPE_CHARGE_START, gun_id, NULL, _recv_seq);

    log_debug("↓↓↓↓收到APP启动充电指令 0x26, gun_id:%d, balance:%d, type:%d, value:%d\r\n", gun_id, account->balance,
        account->type, account->value);

    return 0;
}

/*充电桩应答 APP 启动指令*/
int32_t proto_encode_27(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩应答 APP 启动指令 0x27 gun id:%d, err:%0x\r\n", gun->gun_id, gun->gun_fault_code);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = gun->gun_id;

    if (gun->gun_fault_code != 0)
    {
        data[offset++] = 0x01;
        // 2021-07-09 zhouhao, add fault code when it doesn't equal 0
		data[offset++] = gun->gun_fault_code & 0x00FF;
		data[offset++] = gun->gun_fault_code >> 8;
    }
    else
    {
        data[offset++] = 0x00;
    }

    return offset;
}

/* APP 下发停止充电指令*/
int32_t proto_decode_28(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    cmd_28_t cmd;
    int32_t offset = 0;
    uint8_t gun_index = 0;

    if (data == NULL || charger == NULL)
    {
        return -1;
    }

    memset(&cmd, 0x00, sizeof(cmd));
    memcpy(&(cmd.pile_id), &data[offset], PILE_ID_MAX_LEN);
    offset += PILE_ID_MAX_LEN;
    cmd.gun_id = data[offset++];
    gun_index = cmd.gun_id - 1;

    charger->gun[gun_index].fee.stop_code = CHARGE_STOP_APP;
    event_add_common(&charger->event, EVENT_TYPE_CHARGE_STOP, cmd.gun_id, NULL, _recv_seq);

    log_debug("↓↓↓↓收到APP 下发停止充电指令 0x28 id:%d\r\n", cmd.gun_id);

    return offset;
}

/*充电桩应答 APP 停止指令*/
int32_t proto_encode_29(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩应答 APP 停止指令 0x29, id:%d, err:%0x\r\n", gun->gun_id, gun->gun_fault_code);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = gun->gun_id;

    if (gun->gun_fault_code != 0)
    {
        data[offset++] = 0x01;
    }
    else
    {
        data[offset++] = 0x00;
    }

    return offset;
}

/*充电桩上报VIN码启动充电*/
int32_t proto_encode_2a(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    user_bms_t *u_bms =  gun->bms;
    int32_t offset = 0;

    if (gun == NULL)
    {
        gun->vin_state = VIN_STATE_UNLOCK;
        return -1;
    }

    log_debug("↑↑↑↑发送vim码启动充电0x2a,枪号:%d %d\r\n", gun->gun_id, data_len);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;
    memcpy(&data[offset], u_bms->brm_vin, BRM_VIN_LEN);
    offset += BRM_VIN_LEN;

    return offset;
}

/*云服务器应答VIN码启动*/
int32_t proto_decode_2b(void *param, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)param;
    account_t *account = NULL;
    gun_t *gun = NULL;
    uint8_t gun_id = 0, gun_index = 0, state = 0;

    if (data == NULL)
    {
        gun->vin_state = VIN_STATE_UNLOCK;
        return -1;
    }

    gun_id = data[PILE_ID_MAX_LEN];
    gun_index = gun_id - 1;

    account = (account_t *) & (charger->gun[gun_index].account);
    gun = (gun_t *) & (charger->gun[gun_index]);

    memcpy(&(account->balance), &data[33], 4);
    account->type = TYPE_MONEY;
    account->value = account->balance;
    gun->start_mode = START_MODE_VIN;

    state = data[37];

    if (state == 0)
    {
        event_add_common(&charger->event, EVENT_TYPE_CHARGE_START, gun_id, NULL, 0);
    }

    gun->vin_state = VIN_STATE_UNLOCK;

    log_debug("↓↓↓↓收到云服务器应答VIN码启动0x2a, gun_id:%d, balance:%d, state:%d\r\n", gun_id, account->balance, state);

    return 0;
}


/*充电枪通过APP账号启动充电指令*/
int32_t proto_encode_2c(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    log_debug("↑↑↑↑发送APP账号启动充电0x2c,枪号:%d %d\r\n", gun->gun_id, data_len);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = gun->gun_id;

    memcpy(&data[offset], gun->app_account, APP_ACCOUNT_LEN);
    offset += APP_ACCOUNT_LEN;

    encode_md5(&data[offset], APP_PASSWORD_LEN, gun->app_password, strlen(gun->app_password));
    offset += APP_PASSWORD_LEN;

    return offset;
}

/*云服务器应答APP账号启动指令*/
int32_t proto_decode_2d(void *param, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)param;
    account_t *account = NULL;
    gun_t *gun = NULL;
    uint8_t gun_id = 0, gun_index = 0, state = 0;

    gun_id = data[PILE_ID_MAX_LEN];
    gun_index = gun_id - 1;

    account = (account_t *) & (charger->gun[gun_index].account);
    gun = (gun_t *) & (charger->gun[gun_index]);

    memcpy(&(account->balance), &data[33], 4);
    account->type = TYPE_MONEY;
    account->value = account->balance;
    gun->start_mode = START_MODE_APP_ACCOUNT;

    state = data[37];

    if (state == 0)
    {
        event_add_common(&charger->event, EVENT_TYPE_CHARGE_START, gun_id, NULL, 0);
    }

    gun->app_state = APP_STATE_UNLOCK;

    log_debug("↓↓↓↓收到云服务器应答APP账号启动0x2d, gun_id:%d, balance:%d, state:%d\r\n", gun_id, account->balance, state);

    return 0;
}

/*充电枪通过APP账号下发停止充电指令*/
int32_t proto_encode_2e(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    log_debug("↑↑↑↑发送APP账号停止充电0x2e,枪号:%d %d\r\n", gun->gun_id, data_len);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = gun->gun_id;

    memcpy(&data[offset], gun->app_account, APP_ACCOUNT_LEN);
    offset += APP_ACCOUNT_LEN;

    encode_md5(&data[offset], APP_PASSWORD_LEN, gun->app_password, strlen(gun->app_password));
    offset += APP_PASSWORD_LEN;

    return offset;
}

/*云服务器应答APP账号停止指令*/
int32_t proto_decode_2f(void *param, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)param;
    account_t *account = NULL;
    gun_t *gun = NULL;
    uint8_t gun_id = 0, state = 0;

    gun_id = data[PILE_ID_MAX_LEN];
    state = data[PILE_ID_MAX_LEN + 1];

    if (state == 0)
    {
        event_add_common(&charger->event, EVENT_TYPE_CHARGE_STOP, gun_id, NULL, 0);
    }

    log_debug("↓↓↓↓收到云服务器应答APP账号停止0x2e, gun_id:%d, state:%d\r\n", gun_id, state);

    return 0;
}


/*平台下发启动充电指令*/
int32_t proto_decode_30(void *param, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)param;
    account_t *account = NULL;
    gun_t *gun = NULL;
    uint8_t gun_index = 0, gun_id = 0;

    if (charger == NULL || data == NULL)
    {
        return -1;
    }

    gun_id = data[PILE_ID_MAX_LEN];
    gun_index = gun_id - 1;

    account = (account_t *) & (charger->gun[gun_index].account);
    gun = (gun_t *) & (charger->gun[gun_index]);

    account->type = (balance_type)data[33];
    memcpy(&(account->value), data + 34, 4);

    gun->start_mode = START_MODE_PLATFORM;

    event_add_common(&charger->event, EVENT_TYPE_CHARGE_START, gun_id, NULL, _recv_seq);

    log_debug("↓↓↓↓收到平台下发启动充电指令 0x30, gun_id:%d, type:%d, value:%d\r\n", gun_id, account->type, account->value);

    return 0;
}

/*充电枪应答平台启动指令*/
int32_t proto_encode_31(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩应答 平台启动指令 0x31 gun id:%d, err:%0x\r\n", gun->gun_id, gun->gun_fault_code);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = gun->gun_id;

    if (gun->gun_fault_code != 0)
    {
        data[offset++] = 0x01;
        // 2021-07-09 zhouhao, add fault code when it doesn't equal 0
        data[offset++] = gun->gun_fault_code & 0x00FF;
        data[offset++] = gun->gun_fault_code >> 8;
    }
    else
    {
        data[offset++] = 0x00;
    }


    return offset;
}

/* 平台下发停止停止充电指令*/
int32_t proto_decode_32(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    cmd_28_t cmd;
    int32_t offset = 0;
    uint8_t gun_index = 0;

    if (data == NULL || charger == NULL)
    {
        return -1;
    }

    memset(&cmd, 0x00, sizeof(cmd));
    memcpy(&(cmd.pile_id), &data[offset], PILE_ID_MAX_LEN);
    offset += PILE_ID_MAX_LEN;
    cmd.gun_id = data[offset++];
    gun_index = cmd.gun_id - 1;

    charger->gun[gun_index].fee.stop_code = CHARGE_STOP_PLATFROM;
    event_add_common(&charger->event, EVENT_TYPE_CHARGE_STOP, cmd.gun_id, NULL, _recv_seq);

    log_debug("↓↓↓↓收到平台 下发停止充电指令 0x32id:%d\r\n", cmd.gun_id);

    return offset;
}

/*充电枪应答平台停止指令*/
int32_t proto_encode_33(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩应答 平台停止指令 0x33, id:%d, err:%0x\r\n", gun->gun_id, gun->gun_fault_code);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = gun->gun_id;

    if (gun->gun_fault_code != 0)
    {
        data[offset++] = 0x01;
    }
    else
    {
        data[offset++] = 0x00;
    }

    return offset;
}

/*  云服务器下发充电枪二维码*/
int32_t proto_decode_34(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    gun_t *gun = NULL;
    user_gun_t *u_gun = NULL;
    int32_t offset = 0;
    uint8_t gun_index = 0, gun_id = 0, gun_qr_cnt = 0, i = 0;

    if (data == NULL || p == NULL)
    {
        return -1;
    }
    log_debug("↓↓↓↓收到下发充电枪二维码指令 34 \r\n");

    gun_qr_cnt = data[PILE_ID_MAX_LEN];

    if (gun_qr_cnt != CHARGE_GUN_CNT)
    {
        log_error("二维码个数错误,收到个数:%d,期望个数:%d\r\n", gun_qr_cnt, CHARGE_GUN_CNT);
    }

    offset = PILE_ID_MAX_LEN + 1;
    for (i = 0; i < gun_qr_cnt; i++)
    {
        gun_id = data[offset++];
        gun_index = gun_id - 1;
        gun = (gun_t *) & (charger->gun[gun_index]);
        u_gun = gun->u_gun;
        memcpy(u_gun->qr_code, data + offset, QR_CODE_LEN);
        offset += QR_CODE_LEN;
    }

    event_add_common(&charger->event, EVENT_TYPE_QR_CODE, 0, NULL, _recv_seq);

    return 0;
}

/*充电桩查询充电枪二维码*/
int32_t proto_encode_35(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    int32_t offset = 0;

    if (charger == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩查询充电枪二维码 35\r\n");

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    return offset;
}

/* 云服务器下发充电枪卡枪解除指令 */
int32_t proto_decode_36(void *p, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)p;
    uint8_t gun_id = 0;
    uint8_t gun_index = 0;

    if (data == NULL || charger == NULL)
    {
        return -1;
    }

    gun_id = data[PILE_ID_MAX_LEN];
    gun_index = gun_id - 1;

    charger->gun[gun_index].fee.stop_code = CHARGE_STOP_PLATFROM;
    event_add_common(&charger->event, EVENT_TYPE_GUN_RELEASE, gun_id, NULL, _recv_seq);

    log_debug("↓↓↓↓收到平台 云服务器下发充电枪卡枪解除指令 0x36 id:%d\r\n", gun_id);

    return 0;
}

/* 充电枪应答卡枪解除指令 */
int32_t proto_encode_37(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送 充电枪应答卡枪解除指令 0x37, id:%d, cmd_response_status:%0x\r\n", gun->gun_id, gun->cmd_response_status);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = gun->gun_id;

    data[offset++] = gun->cmd_response_status;

    return offset;
}

/*充电桩上报 BMS 参数配置阶段数据*/
int32_t proto_encode_50(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    user_bms_t *bms = gun->bms;
    int32_t offset = 0;

    if (bms == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩上报 BMS 参数配置阶段数据 0x50, id:%d\r\n", gun->gun_id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;
    memcpy(&data[offset], bms->brm_bms_connect_version, BRM_VERSION_LEN);
    offset += BRM_VERSION_LEN;
    data[offset++] = bms->brm_battery_type;
    memcpy(&data[offset], &bms->brm_battery_rated_capacity, sizeof(bms->brm_battery_rated_capacity));
    offset += sizeof(bms->brm_battery_rated_capacity);
    memcpy(&data[offset], &bms->brm_battery_volt, sizeof(bms->brm_battery_volt));
    offset += sizeof(bms->brm_battery_volt);
    memcpy(&data[offset], &bms->brm_battery_supplier, BRM_BATT_SUP_LEN);
    offset += BRM_BATT_SUP_LEN;
    memcpy(&data[offset], &bms->brm_battery_no, sizeof(bms->brm_battery_no));
    offset += sizeof(bms->brm_battery_no);
    memcpy(&data[offset], &bms->brm_battery_produce_date, BRM_BATT_DATE_LEN);
    offset += BRM_BATT_DATE_LEN;
    memcpy(&data[offset], &bms->brm_battery_charge_count, sizeof(bms->brm_battery_charge_count));
    offset += sizeof(bms->brm_battery_charge_count);
    data[offset++] = bms->brm_battery_property_id;
    offset++;//预留
    memcpy(&data[offset], &bms->brm_bms_software_version, BRM_BMS_SOFT_VER_LEN);
    offset += BRM_BMS_SOFT_VER_LEN;
    memcpy(&data[offset], bms->brm_vin, BRM_VIN_LEN);
    offset += BRM_VIN_LEN;
    memcpy(&data[offset], &bms->bcp_max_voltage, sizeof(bms->bcp_max_voltage));
    offset += sizeof(bms->bcp_max_voltage);
    memcpy(&data[offset], &bms->bcp_max_current, sizeof(bms->bcp_max_current));
    offset += sizeof(bms->bcp_max_current);
    memcpy(&data[offset], &bms->bcp_max_power, sizeof(bms->bcp_max_power));
    offset += sizeof(bms->bcp_max_power);
    memcpy(&data[offset], &bms->bcp_total_voltage, sizeof(bms->bcp_total_voltage));
    offset += sizeof(bms->bcp_total_voltage);
    data[offset++] = bms->bcp_max_temperature;
    memcpy(&data[offset], &bms->bcp_battery_soc, sizeof(bms->bcp_battery_soc));
    offset += sizeof(bms->bcp_battery_soc);
    memcpy(&data[offset], &bms->bcp_battery_soc_current_voltage, sizeof(bms->bcp_battery_soc_current_voltage));
    offset += sizeof(bms->bcp_battery_soc_current_voltage);
    data[offset++] = bms->bro_bms_is_ready;
    data[offset++] = bms->cro_is_ready;

    return offset;
}

/*充电桩上报BMS充电阶段数据*/
int32_t proto_encode_51(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    user_bms_t *bms = gun->bms;
    int32_t offset = 0;

    if (bms == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩上报 BMS 充电阶段数据 0x51, id:%d\r\n", gun->gun_id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;
    memcpy(&data[offset], &bms->bcl_voltage_need, sizeof(bms->bcl_voltage_need));
    offset += sizeof(bms->bcl_voltage_need);
    memcpy(&data[offset], &bms->bcl_current_need, sizeof(bms->bcl_current_need));
    offset += sizeof(bms->bcl_current_need);
    data[offset++] = bms->bcl_charge_mode;
    memcpy(&data[offset], &bms->bcs_test_voltage, sizeof(bms->bcs_test_voltage));
    offset += sizeof(bms->bcs_test_voltage);
    memcpy(&data[offset], &bms->bcs_test_current, sizeof(bms->bcs_test_current));
    offset += sizeof(bms->bcs_test_current);
    memcpy(&data[offset], &bms->bcs_max_single_voltage_and_no, sizeof(bms->bcs_max_single_voltage_and_no));
    offset += sizeof(bms->bcs_max_single_voltage_and_no);
    data[offset++] = bms->bcs_current_soc;
    memcpy(&data[offset], &bms->bcs_charge_time_left, sizeof(bms->bcs_charge_time_left));
    offset += sizeof(bms->bcs_charge_time_left);
    data[offset++] = bms->bsm_max_single_voltage_and_no;
    data[offset++] = bms->bsm_max_temperature;
    data[offset++] = bms->bsm_max_temperature_check_no;
    data[offset++] = bms->bsm_min_temperature;
    memcpy(&data[offset], &bms->bsm_min_temperature_check_no, sizeof(bms->bsm_min_temperature_check_no));
    offset += sizeof(bms->bsm_min_temperature_check_no);
    data[offset++] = bms->bsm_voltage_too_high_or_too_low;
    data[offset++] = bms->bsm_car_battery_soc_too_high_or_too_low;
    data[offset++] = bms->bsm_car_battery_charge_over_current;
    data[offset++] = bms->bsm_battery_temperature_too_high;
    data[offset++] = bms->bsm_battery_insulation_state;
    data[offset++] = bms->bsm_battery_connect_state;
    data[offset++] = bms->bsm_allow_charge;

    return offset;
}

/*充电桩上报 充电中止BMS-CST数据*/
int32_t proto_encode_52(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    user_bms_t *bms = gun->bms;
    int32_t offset = 0;

    if (bms == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩上报 充电中止BMS-CST数据 0x52, id:%d\r\n", gun->gun_id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;

    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;

    data[offset++] = bms->cst_stop_reason;
    memcpy(&data[offset], &bms->cst_fault_reason, sizeof(bms->cst_fault_reason));
    offset += sizeof(bms->cst_fault_reason);
    data[offset++] = bms->cst_error_reason;

    return offset;
}

/*充电桩上报 充电中止BMS-BST数据*/
int32_t proto_encode_53(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    user_bms_t *bms = gun->bms;
    int32_t offset = 0;

    if (bms == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩上报 充电中止BMS-BST数据 0x53, id:%d\r\n", gun->gun_id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;

    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;

    data[offset++] = bms->bst_stop_reason;
    memcpy(&data[offset], &bms->bst_fault_reason, sizeof(bms->bst_fault_reason));
    offset += sizeof(bms->bst_fault_reason);
    data[offset++] = bms->bst_error_reason;

    return offset;
}

/*充电桩上报 BMS 充电结束阶段数据*/
int32_t proto_encode_54(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    user_bms_t *bms = gun->bms;
    int32_t offset = 0;

    if (bms == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩上报 BMS 充电结束阶段数据 0x54, id:%d\r\n", gun->gun_id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;
    data[offset++] = bms->bsd_stop_soc;
    memcpy(&data[offset], &bms->bsd_battery_low_voltage, sizeof(bms->bsd_battery_low_voltage));
    offset += sizeof(bms->bsd_battery_low_voltage);
    memcpy(&data[offset], &bms->bsd_battery_high_voltage, sizeof(bms->bsd_battery_high_voltage));
    offset += sizeof(bms->bsd_battery_high_voltage);
    data[offset++] = bms->bsd_battery_low_temperature;
    data[offset++] = bms->bsd_battery_high_temperature;
    data[offset++] = bms->bem_error_spn2560_00;
    data[offset++] = bms->bem_error_spn2560_aa;
    data[offset++] = bms->bem_error_time_sync;
    data[offset++] = bms->bem_error_ready_to_charge;
    data[offset++] = bms->bem_error_receive_status;
    data[offset++] = bms->bem_error_receive_stop;
    data[offset++] = bms->bem_error_receive_statistics;
    data[offset++] = bms->bem_error_other;

    return offset;
}

/*充电桩上报账单*/
int32_t proto_encode_60(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    fee_t *fee = (fee_t *) & (gun->fee);
    user_bms_t *u_bms = gun->bms;
    int32_t offset = 0;

    //TDateTime tm_now;
    //read_current_datetime(&tm_now);

    //printf("time_now:%4d %2d %2d %2d %2d %2d\r\n",tm_now.Year,tm_now.Week,tm_now.Day,tm_now.Hour,tm_now.Min,tm_now.Sec);
    if (gun == NULL || fee == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩上报账单 0x60, id:%d\r\n", gun->gun_id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;

    //printf("?????start_time %ld\r\n",fee->start_time);
    //printf("?????end_time   %ld\r\n",fee->end_time);

    proto_encode_cp56time2a(&data[offset], 7, fee->start_time);
    offset += 7;
    proto_encode_cp56time2a(&data[offset], 7, fee->end_time);
    offset += 7;
    data[offset++] = gun->start_mode;
    data[offset++] = 1;//连云成功，状态一定是在线

    data[offset++] = gun->u_gun->soc;

    memcpy(&data[offset], &fee->total_time, sizeof(fee->total_time));
    offset += sizeof(fee->total_time);

    memcpy(&data[offset], &fee->total_kwh, sizeof(fee->total_kwh));
    offset += sizeof(fee->total_kwh);

    memcpy(&data[offset], &gun->u_gun->lose_charge_kwh, sizeof(gun->u_gun->lose_charge_kwh));
    offset += sizeof(gun->u_gun->lose_charge_kwh);

    uint32_t total_charge_money = (fee->total_fee_elec + 5000) / 10000;
    memcpy(&data[offset], &total_charge_money, sizeof(total_charge_money));
    offset += sizeof(total_charge_money);

    uint32_t total_service_money = (fee->total_fee_service + 5000) / 10000;
    memcpy(&data[offset], &total_service_money, sizeof(total_service_money));
    offset += sizeof(total_service_money);

    memcpy(&data[offset], gun->card_id, CARD_ID_LEN);
    offset += CARD_ID_LEN;

    memcpy(&data[offset], u_bms->brm_vin, BRM_VIN_LEN);
    offset += BRM_VIN_LEN;

    data[offset++] = U16_LO(fee->stop_code);
    data[offset++] = U16_HI(fee->stop_code);

    memcpy(&data[offset], gun->fee.period_power, sizeof(gun->fee.period_power));
    offset += sizeof(gun->fee.period_power);

    memcpy(&data[offset], &gun->fee.price_info, sizeof(gun->fee.price_info));
    offset += sizeof(gun->fee.price_info);

    log_debug("账单:\r\n");
    log_debug("枪号:%d, 启动:%d, 类型:%d, SOC:%d, 原因:%04x\r\n", \
        gun->gun_id, gun->start_mode, 1, gun->u_gun->soc, fee->stop_code);
    log_debug("时长:%d\r\n", fee->total_time);
    log_debug("电度:%d\r\n", fee->total_kwh / 10000);
    log_debug("电费:%d\r\n", fee->total_fee_elec / 10000);
    log_debug("服务费:%d\r\n", fee->total_fee_service / 10000);

    log_debug("分段电量:\r\n");

    int i = 0;
    for (i = 0; i < PERIOD_LEN; i++)
    {
        printf("%d ", gun->fee.period_power[i]);
    }
    log_debug("\r\n");

    return offset;
}

/*云服务器确认账单*/
int32_t proto_decode_61(void *param, uint8_t *data, uint16_t data_len)
{
    cmd_61_t cmd = {0};
    os_timestamp_t tt = 0;

    if (param == NULL || data == NULL || data_len != sizeof(cmd_61_t))
    {
        return -1;
    }

    memcpy(&cmd, (cmd_61_t *)data, data_len);
    tt = proto_decode_cp56time2a(&data[33], 7);

    log_debug("↓↓↓↓收到云服务器确认账单 0x61, id:%d, state:%d, time:%d\r\n", cmd.gun_id, cmd.confirm_state, tt);

    return 0;
}

/*云服务器下发电费计价策略*/
int32_t proto_decode_70(void *param, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)param;
    int32_t offset = 0;

    if (charger == NULL || data == NULL || \
        data_len != (sizeof(price_t) + PILE_ID_MAX_LEN))
    {
        return -1;
    }

    log_debug("↓↓↓↓收到云服务器下发电费计价策略 0x70\r\n");

    price_set(charger, (price_t *)(data + PILE_ID_MAX_LEN));

    return offset;
}

/*充电桩查询电费计价策略*/
int32_t proto_encode_71(void *param, uint8_t *data, uint16_t data_len)
{
    int32_t offset = 0;

    if (data == NULL || data_len <= PILE_ID_MAX_LEN)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩查询电费计价策略 0x71\r\n");

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    return offset;
}

//地锁实时状态数据
int32_t proto_encode_90(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    user_lock_t *lock = gun->lock;

    int32_t offset = 0;

    if (data == NULL || lock == NULL || data_len < 42)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送地锁实时状态数据 0x90 id:%d state:%d, battery:%d\r\n", lock->id, lock->state, lock->battery);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = lock->id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;
    data[offset++] = lock->state;
    data[offset++] = lock->battery;

    return offset;
}

//云服务器下发控制地锁指令
int32_t proto_decode_91(void *param, uint8_t *data, uint16_t data_len)
{
#if SUPPORT_LOCK
    charger_t *charger = (charger_t *)param;
    cmd_91_t cmd;
    uint8_t gun_index = 0;

    if (charger == NULL || data == 0 || data_len != sizeof(cmd_91_t))
    {
        return -1;
    }

    memset(&cmd, 0x00, sizeof(cmd));
    memcpy(&cmd, data, data_len);
    cmd.gun_id = cmd.gun_id;
    gun_index = cmd.gun_id - 1;

    charger->gun[gun_index].lock_switch = cmd._switch;

    event_add_common(&charger->event, EVENT_TYPE_LOCK_SWITCH, cmd.gun_id, &charger->gun[gun_index].lock_switch, _recv_seq);

    log_debug("↓↓↓↓收到云服务器下发控制地锁指令 0x91,id:%d, switch:%d\r\n", cmd.gun_id, cmd._switch);
    return 0;
#else
    log_error("↓↓↓↓收到云服务器下发控制地锁指令 0x91，不支持!\r\n");
    return -1;
#endif

}

//充电桩应答控制地锁指令
int32_t proto_encode_92(void *param, uint8_t *data, uint16_t data_len)
{
#if SUPPORT_LOCK
    gun_t *gun = (gun_t *)param;
    user_lock_t *lock = NULL;
    int32_t offset = 0;

    if (data == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送应答控制地锁指令 0x92, id:%d\r\n", lock->id);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    lock = gun->lock;

    data[offset++] = lock->id;
    data[offset++] = lock->state;

    data[offset++] = gun->lock_fault_code;

    return offset;
#else
    log_error("↑↑↑↑不支持地锁指令\r\n");
    return 0;
#endif
}

//云服务器下发同步时间数据
int32_t proto_decode_b0(void *c, uint8_t *data, uint16_t data_len)
{
    int32_t offset = 0;
    uint32_t ts = 0;
    os_time_t tm = {0};

    if (data == NULL || data_len != (PILE_ID_MAX_LEN + 7))
    {
        return -1;
    }

    log_debug("↓↓↓↓收到同步时间 0xb0\r\n");

    offset += PILE_ID_MAX_LEN;

    ts = proto_decode_cp56time2a(&data[offset], 7);

    os_server_ts_to_time(ts, &tm);

    user_cb_set_time(ts, &tm);

    offset += 7;

    return 0;
}

//充电桩发送同步时间请求
int32_t proto_encode_b1(void *p, uint8_t *data, uint16_t data_len)
{
    int32_t offset = 0;

    if (data == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送同步时间请求 0xb1\r\n");

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    return offset;
}

//故障数据
int32_t proto_encode_c0(void *param, uint8_t *data, uint16_t data_len)
{
    gun_t *gun = (gun_t *)param;
    int32_t offset = 0;

    if (gun == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送故障数据 0xc0 id:%d, fault:%x\r\n", gun->gun_id + 1, gun->gun_fault_code);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;

    data[offset++] = U16_LO(gun->gun_fault_code);
    data[offset++] = U16_HI(gun->gun_fault_code);

    return offset;
}

//故障恢复
int32_t proto_encode_c1(void *param, uint8_t *data, uint16_t data_len)
{
    int32_t offset = 0;
    gun_t *gun = (gun_t *)param;
    if (data == NULL)
    {
        return -1;
    }

    log_debug("↑↑↑↑发送故障恢复 0xc1, id:%d\r\n", gun->gun_id + 1);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;
    data[offset++] = gun->gun_id;
    proto_encode_cp56time2a(&data[offset], 7, os_get_time());
    offset += 7;

    return offset;
}

/************************************************************************************************************
 *                                  OTA相关
************************************************************************************************************/
//云服务器下发升级指令
int32_t proto_decode_d1(void *c, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)c;
    OTA_RSP_STATE default_state = OTA_ACCEPT;
    int32_t i = 0;

    log_debug("↓↓↓↓收到OTA D1指令\r\n");

    if (data == NULL || data_len != CMD_D1_LEN)
    {
        log_error("proto_decode_d1 need len=%d, actual len=%d\r\n", CMD_D1_LEN, data_len);
        return -1;
    }

    /* 只有在空闲状态下才能升级 */
    for (i = 0; i < CHARGE_GUN_CNT; i++)
    {
        if (charger->gun[i].gun_state != GUN_STATE_IDLE)
        {
            default_state = OTA_REJECT;
            goto out;
        }
    }

    //不使用断点续传了，每次都重新开始
    memset(charger->ota_info.file_name, 0, sizeof(charger->ota_info.file_name));
    memcpy(charger->ota_info.file_name, data + PILE_ID_MAX_LEN, OTA_FILE_NAME_LEN);
    charger->ota_info.file_total_len = BYTES_TO_UINT32_LITTLE((data + PILE_ID_MAX_LEN + OTA_FILE_NAME_LEN));
    charger->ota_info.file_crc = BYTES_TO_UINT16_LITTLE((data + PILE_ID_MAX_LEN + OTA_FILE_NAME_LEN + 4));
    charger->ota_info.calculation_crc = 0xFFFF;
    charger->ota_info.file_offset = 0x00;
    log_debug("file_name=%s\r\n", charger->ota_info.file_name);
    log_debug("file_total_len=%d\r\n", charger->ota_info.file_total_len);
    log_debug("file_crc=%x\r\n", charger->ota_info.file_crc);

out:
    charger->ota_info.rsp_state = default_state;
    event_add_common(&charger->event, EVENT_TYPE_OTA_RSP, 0, NULL, _recv_seq);

    return 0;
}

//充电桩应答升级指令
int32_t proto_encode_d2(void *param, uint8_t *data, uint16_t data_len)
{
    ota_info_t *ota_info = (ota_info_t *)param;
    uint32_t offset = 0x00;

    log_debug("↑↑↑↑回复OTA D2指令\r\n");

    if (data == NULL || data_len < CMD_D2_LEN)
    {
        log_error("proto_encode_d2 need len=%d, actual len=%d\r\n", CMD_D2_LEN, data_len);
        return -1;
    }

    log_debug("↑↑↑↑发送充电桩应答升级指令 0xd2, state:%d\r\n", ota_info->rsp_state);

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = ota_info->rsp_state;

    if (ota_info->rsp_state == OTA_ACCEPT)
    {
        os_create_download_file(ota_info->file_name);
    }

    return offset;
}

//充电桩请求下载升级包
int32_t proto_encode_d3(void *c, uint8_t *data, uint16_t data_len)
{
    ota_info_t *ota_info = (ota_info_t *)c;
    uint32_t remain_length = 0x00, offset = 0x00;

    log_debug("↑↑↑↑回复OTA D3指令\r\n");

    if (data == NULL || data_len < CMD_D3_LEN)
    {
        log_error("proto_encode_d3 need len=%d, actual len=%d\r\n", CMD_D3_LEN, data_len);
        return -1;
    }

    //剩余文件大小
    remain_length = ota_info->file_total_len - ota_info->file_offset;

    //每包请求大小
    ota_info->file_req_length = (remain_length >= OTA_MAX_PACKAGE_LEN) ? OTA_MAX_PACKAGE_LEN : remain_length;

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    proto_uint32_to_bytes_little(&data[offset], ota_info->file_offset);
    offset += sizeof(ota_info->file_offset);

    proto_uint16_to_bytes_little(&data[offset], ota_info->file_req_length);
    offset += sizeof(ota_info->file_req_length);

    log_debug("file_offset:%d, file_req_length:%d\r\n", ota_info->file_offset, ota_info->file_req_length);

    return offset;
}

//云服务器应答下载请求
int32_t proto_decode_d4(void *param, uint8_t *data, uint16_t data_len)
{
    charger_t *charger = (charger_t *)param;
    ota_info_t *ota_info = (ota_info_t *) & (charger->ota_info);
    uint8_t is_last = 0x00;
    uint32_t offset = 0x00;
    uint16_t package_len = 0x00;

    log_debug("↓↓↓↓收到OTA D4指令\r\n");

    if (data == NULL || data_len != (CMD_D4_MIN_LEN + ota_info->file_req_length))
    {
        log_error("proto_decode_d4 need len=%d, actual len=%d\r\n", CMD_D4_MIN_LEN + ota_info->file_req_length, data_len);
        return -1;
    }

    //是否最后一包
    is_last = data[32];
    offset = BYTES_TO_UINT32_LITTLE((data + 33));
    package_len = BYTES_TO_UINT16_LITTLE((data + 37));

    if (ota_info->file_offset == offset && ota_info->file_req_length == package_len)
    {
        ota_info->repeated_requests_num = 0;
        if (os_write_download_file_data(ota_info->file_name, offset, data + 39, package_len) == 0)
        {
            if ((ota_info->file_offset + package_len) <= ota_info->file_total_len)
            {
                ota_info->file_offset += package_len;
                ota_info->calculation_crc = proto_crc16(data + 39, package_len, ota_info->calculation_crc);
            }
            else
            {
                log_error("error file_total_len=%d, download=%d\r\n", ota_info->file_total_len, \
                    ota_info->file_offset + package_len);

                //直接协议终止,返回最后一包
                is_last = 0x01;
            }
        }
        else
        {
            //直接协议终止,返回最后一包
            is_last = 0x01;
        }
    }
    else
    {
        log_error("error package info up file_offset=%d, package_len=%d, down file_offset=%d, package_len=%d\r\n", \
            ota_info->file_offset, ota_info->file_req_length, offset, package_len);

        if (ota_info->repeated_requests_num++ == OTA_REPEATED_REQUESTS_NUM)
        {
            log_error("reach max OTA_REPEATED_REQUESTS_NUM=%d, stop ota\r\n", OTA_REPEATED_REQUESTS_NUM);
            //考虑错误太多,可能数据已经乱掉,重新下载
            memset(ota_info, 0, sizeof(ota_info_t));
            return -1;
        }
    }

    if (is_last)
    {
        log_debug("file_crc=%x calculation_crc=%x\r\n", ota_info->file_crc, ota_info->calculation_crc);
        ota_info->download_state = (ota_info->file_crc == ota_info->calculation_crc) ? OTA_SUCCESS : OTA_FAILED;
        event_add_common(&charger->event, EVENT_TYPE_OTA_DOWNLOAD_DONE, 0, NULL, _recv_seq);
        os_download_file_done(ota_info->file_name, ota_info->download_state);
    }
    else
    {
        event_add_common(&charger->event, EVENT_TYPE_OTA_DOWNLOADING, 0, NULL, _recv_seq);
    }

    return 0;
}

//充电桩发送升级下载结果
int32_t proto_encode_d5(void *param, uint8_t *data, uint16_t data_len)
{
    ota_info_t *ota_info = (ota_info_t *)param;
    uint32_t offset = 0x00;

    log_debug("↑↑↑↑回复OTA D5指令\r\n");

    if (data == NULL || data_len < CMD_D5_LEN)
    {
        log_error("proto_encode_d5 need len=%d, actual len=%d\r\n", CMD_D5_LEN, data_len);
        return -1;
    }

    strncpy((char *)&data[offset], CHARGE_PILE_ID, strlen(CHARGE_PILE_ID));
    offset += PILE_ID_MAX_LEN;

    data[offset++] = ota_info->download_state;

    return offset;
}

int32_t proto_decode_ff(void *c, uint8_t *data, uint16_t data_len)
{
    if (data == NULL || data_len != 1)
    {
        return -1;
    }

    log_debug("↓↓↓↓收到数据格式错误回复 0xff\r\n");

    switch (data[0])
    {
    case 1:
        log_error("1: 标识域不正确\r\n");
        break;
    case 2:
        log_error("2: 版本域不正确\r\n");
        break;
    case 3:
        log_error("3: 不支持的opcode类型\r\n");
        break;
    case 4:
        log_error("4: 数据长度不正确\r\n");
        break;
    case 5:
        log_error("5: crc校验错误\r\n");
        break;
    default:
        log_error("未知错误:%d\r\n", data[0]);
        break;

    }
    return 0;
}

static cmd_map_t _proto_cmd_map[] =
{
    { CMD_F0, CMD_F1, CMD_TYPE_UP_RESP,         proto_encode_f0, proto_decode_f1 },
    { CMD_F2, CMD_F3, CMD_TYPE_UP,              proto_encode_f2, proto_decode_f3 },
    { CMD_F4, CMD_F5, CMD_TYPE_DOWN_RESP,       proto_encode_f4, proto_decode_f5 },
    { CMD_01, CMD_00, CMD_TYPE_DOWN_RESP,       proto_encode_01, proto_decode_00 },
    { CMD_02, CMD_00, CMD_TYPE_UP,              proto_encode_02, NULL            },
    { CMD_04, CMD_03, CMD_TYPE_DOWN_RESP,       proto_encode_04, proto_decode_03 },
    { CMD_06, CMD_05, CMD_TYPE_DOWN_RESP,       proto_encode_06, proto_decode_05 },
    { CMD_08, CMD_07, CMD_TYPE_DOWN_RESP,       proto_encode_08, proto_decode_07 },
    { CMD_21, CMD_00, CMD_TYPE_UP,              proto_encode_21, NULL            },
    { CMD_22, CMD_23, CMD_TYPE_UP_RESP,         proto_encode_22, proto_decode_23 },
    { CMD_24, CMD_25, CMD_TYPE_UP_RESP,         proto_encode_24, proto_decode_25 },
    { CMD_27, CMD_26, CMD_TYPE_DOWN_RESP,       proto_encode_27, proto_decode_26 },
    { CMD_29, CMD_28, CMD_TYPE_DOWN_RESP,       proto_encode_29, proto_decode_28 },
    { CMD_2A, CMD_2B, CMD_TYPE_UP_RESP,         proto_encode_2a, proto_decode_2b },
    { CMD_2C, CMD_2D, CMD_TYPE_UP_RESP,         proto_encode_2c, proto_decode_2d },
    { CMD_2E, CMD_2F, CMD_TYPE_UP_RESP,         proto_encode_2e, proto_decode_2f },
    { CMD_31, CMD_30, CMD_TYPE_DOWN_RESP,       proto_encode_31, proto_decode_30 },
    { CMD_33, CMD_32, CMD_TYPE_DOWN_RESP,       proto_encode_33, proto_decode_32 },
    { CMD_35, CMD_34, CMD_TYPE_UP_RESP_OR_DOWN, proto_encode_35, proto_decode_34 },
    { CMD_37, CMD_36, CMD_TYPE_DOWN_RESP,       proto_encode_37, proto_decode_36 },
    { CMD_50, CMD_00, CMD_TYPE_UP,              proto_encode_50, NULL            },
    { CMD_51, CMD_00, CMD_TYPE_UP,              proto_encode_51, NULL            },
    { CMD_52, CMD_00, CMD_TYPE_UP,              proto_encode_52, NULL            },
    { CMD_53, CMD_00, CMD_TYPE_UP,              proto_encode_53, NULL            },
    { CMD_54, CMD_00, CMD_TYPE_UP,              proto_encode_54, NULL            },
    { CMD_60, CMD_61, CMD_TYPE_UP_RESP,         proto_encode_60, proto_decode_61 },
    { CMD_71, CMD_70, CMD_TYPE_UP_RESP_OR_DOWN, proto_encode_71, proto_decode_70 },
    { CMD_90, CMD_00, CMD_TYPE_UP,              proto_encode_90, NULL            },
    { CMD_92, CMD_91, CMD_TYPE_DOWN_RESP,       proto_encode_92, proto_decode_91 },
    { CMD_B1, CMD_B0, CMD_TYPE_UP,              proto_encode_b1, proto_decode_b0 },
    { CMD_C0, CMD_00, CMD_TYPE_UP,              proto_encode_c0, NULL            },
    { CMD_C1, CMD_00, CMD_TYPE_UP,              proto_encode_c1, NULL            },
    { CMD_D2, CMD_D1, CMD_TYPE_DOWN_RESP,       proto_encode_d2, proto_decode_d1 },
    { CMD_D3, CMD_D4, CMD_TYPE_UP_RESP,         proto_encode_d3, proto_decode_d4 },
    { CMD_D5, CMD_00, CMD_TYPE_UP,              proto_encode_d5, NULL },
    { CMD_00, CMD_FF, CMD_TYPE_DOWN,            NULL, proto_decode_ff },
};

int8_t proto_get_send_cmd_index(uint8_t cmd)
{
    int8_t i = 0;

    for (i = 0; i < PROTO_CMD_MAP_CNT; i++)
    {
        if (_proto_cmd_map[i].cmd_send == cmd)
        {
            return i;
        }
    }

    log_error("err: can`t find send cmd[%02x] id!\r\n", cmd);

    return -1;
}

int8_t proto_get_recv_cmd_index(uint8_t cmd)
{
    int8_t i = 0;

    for (i = 0; i < PROTO_CMD_MAP_CNT; i++)
    {
        if (_proto_cmd_map[i].cmd_recv == cmd && _proto_cmd_map[i].decode_cb != NULL)
        {
            return i;
        }
    }

    log_error("err: can`t find recv cmd[%02x] id!\r\n", cmd);

    return -1;
}

/*通过ack的命令查找发送的cmd*/
int8_t proto_get_send_cmd_by_recv_cmd(uint8_t cmd)
{
    int8_t i = 0;

    for (i = 0; i < PROTO_CMD_MAP_CNT; i++)
    {
        if (_proto_cmd_map[i].cmd_recv == cmd)
        {
            return _proto_cmd_map[i].cmd_send;
        }
    }

    return -1;
}

//clean all recv buffer data.
void net_rcv_buf_clean_all(proto_rcv_buf_t *rb)
{
    rb->curr = rb->start;
    rb->free_len = rb->size;
    log_debug("rcv_buf_clean_all:%d\r\n", rb->free_len);
}

//clean all queue data.
void net_queue_clean_all(net_queue_t *q)
{
    q->curr = q->start;
    q->item_info = q->end;
    q->free_len = net_queue_free_len(q);
    log_debug("net_queue_clean_all:%d\r\n", q->free_len);
}

//clean the data which has been sent.
void net_queue_clean_sent(net_queue_t *q)
{
    net_queue_item_t *new_head;

    for (new_head = net_queue_get_item(q, 0); new_head >= q->item_info; --new_head)
    {
        if (new_head->state != PROTO_QUEUED_COMPLETE)
        {
            break;
        }
    }

    /* check if everything can be removed */
    if (new_head < q->item_info)
    {
        q->curr = q->start;
        q->item_info = q->end;
        q->free_len = net_queue_free_len(q);
        return;
    }
    else if (new_head == net_queue_get_item(q, 0))
    {
        /* do nothing */
        return;
    }

    /* move buffered data */
    uint32_t n = q->curr - new_head->start;
    uint32_t removing = new_head->start - (uint8_t *) q->start;
    memmove(q->start, new_head->start, n);
    q->curr = (uint8_t *)q->start + n;

    /* move queue */
    uint32_t new_tail_idx = new_head - q->item_info;
    memmove(net_queue_get_item(q, new_tail_idx), q->item_info, sizeof(net_queue_item_t) * (new_tail_idx + 1));
    q->item_info = net_queue_get_item(q, new_tail_idx);
    uint32_t i = 0;

    /* bump back start's */
    for (i = 0; i < new_tail_idx + 1; ++i)
    {
        net_queue_get_item(q, i)->start -= removing;
    }

    /* get free_len */
    q->free_len = net_queue_free_len(q);
}

int32_t net_queue_en(net_queue_t *q, uint16_t data_len, uint8_t cmd, uint16_t seq)
{
    /* make queued item header */
    --(q->item_info);
    q->item_info->start         = q->curr;
    q->item_info->size          = data_len;
    q->item_info->state         = PROTO_QUEUED_UNSENT;
    q->item_info->cmd           = cmd;
    q->item_info->seq           = seq;
    q->item_info->send_times    = 0;

    /* move curr and recalculate curr_sz */
    q->curr += data_len;
    q->free_len = net_queue_free_len(q);

    return 0;
}

static net_queue_item_t *net_queue_find(net_queue_t *q, uint8_t cmd, uint16_t ack)
{
    net_queue_item_t *curr;

    uint8_t send_cmd = proto_get_send_cmd_by_recv_cmd(cmd);

    for (curr = net_queue_get_item(q, 0); curr >= q->item_info; --curr)
    {
        // if (curr->cmd == send_cmd && curr->state == PROTO_QUEUED_AWAITING_ACK && curr->seq == ack)
    	// 临时去掉检查，避免重发异常
    	if (curr->cmd == send_cmd && curr->state == PROTO_QUEUED_AWAITING_ACK)
        {
            return curr;
        }
    }

    return NULL;
}

void net_queue_init(net_queue_t *q, uint8_t *buf, uint32_t bufsz)
{
    q->start = buf;
    q->end = (int8_t *)buf + bufsz;
    q->curr = buf;
    q->item_info = (net_queue_item_t *)q->end;
    q->free_len = net_queue_free_len(q);
}

int32_t net_disconnect(net_client_t *client)
{
    int32_t ret = 0;
    if (client == NULL || client->sock_fd == -1)
    {
        return -1;
    }

    ret = _tcp_close(client->sock_fd);
    client->sock_fd = -1;

    return ret;
}

int32_t net_connect(net_client_t *client)
{
    if (client == NULL)
    {
        return -1;
    }

    client->sock_fd = _tcp_open((const int8_t *)gt_para_from_main.server_ip, gt_para_from_main.server_port);

    if (client->sock_fd == -1)
    {
        return -1;
    }
    else
    {
        client->net_status = NET_STATUS_CONNECTED;
    }

    return 0;
}

static int32_t net_recv(charger_t *charger)
{
    charge_protocol protocol;
    int32_t ret = 0;
    int8_t cmd_index = 0;
    int8_t bill_index = 0;
    uint8_t *p_plain_data = NULL;
    uint32_t plain_data_len = 0;
    void *dest = NULL, *src  = NULL;
    uint32_t n = 0;
    net_client_t *client = &charger->net_client;
    net_queue_item_t *item = NULL;
    static uint16_t expect_seq = 0;

    //check if data has not been received for charger->param.period_heartbeat * charger->param.heartbeat_timeout_cnt
    if ((os_get_time() - client->time_of_last_recv) > 0 \
        && (os_get_time() - client->time_of_last_recv) > charger->local_cfg.param.period_heartbeat * charger->local_cfg.param.heartbeat_timeout_cnt\
        && client->time_of_last_recv != 0)
    {
        log_debug("超过[%d]秒未收到数据,断开连接%d-%d\r\n", charger->local_cfg.param.period_heartbeat * charger->local_cfg.param.heartbeat_timeout_cnt, os_get_time(), client->time_of_last_recv);
        client->time_of_last_recv = 0;
        return -1;
    }

    while (1)
    {
        ret = _tcp_read(client->sock_fd, (int8_t *)client->rcv_buf.curr, client->rcv_buf.free_len, 10);

        if (ret < 0)
        {
            log_error("_tcp_read:%d, free_len:%d\r\n", ret, client->rcv_buf.free_len);
            return ret;
        }
        client->rcv_buf.curr += ret;
        client->rcv_buf.free_len -= ret;

        ret = proto_decode_pack(client->rcv_buf.start, client->rcv_buf.curr - client->rcv_buf.start, &protocol, &cmd_index);
        if (ret < 0)
        {
            log_error("Failed to receive data validation! : %d, data len:%d\r\n", ret, client->rcv_buf.curr - client->rcv_buf.start);
#if IS_DEBUG_HEX
            proto_hex_dump("data", client->rcv_buf.start, client->rcv_buf.curr - client->rcv_buf.start);
#endif
            return ret;
        }
        else if (ret == 0)    //not receive a complete packet
        {
            if (client->rcv_buf.free_len == 0)
            {
                log_error("recv buffer space not enough!\r\n");
                return ERR_PROTOCOL_BUFFER_NOT_ENOUGH;
            }
            return ERR_SUCCESS;
        }
        else    //receive at least one packet
        {
            client->time_of_last_recv = os_get_time();

            p_plain_data = protocol.data;
            plain_data_len = protocol.length;

#if SUPPORT_CRYPT
            if (protocol.cmd != CMD_F1 && protocol.cmd != 0xFF)
            {
                memset(client->crypt_buf, 0, sizeof(client->crypt_buf));
                plain_data_len = sizeof(client->crypt_buf);
                if (client->decrypt(protocol.data, protocol.length, client->crypt_buf, &plain_data_len, client->crypt_key) < 0)
                {
                    log_error("decrypt data err\r\n");
                    goto release_buf;
                }
                else
                {
                    p_plain_data = client->crypt_buf;
                }
            }
#endif
#if IS_DEBUG_HEX
            proto_hex_dump("RECV HEX DATA", client->rcv_buf.start, client->rcv_buf.curr - client->rcv_buf.start);
#endif

            /*     decode seq         */
            memcpy((uint8_t *) & (protocol.seq), p_plain_data, sizeof(protocol.seq));
            p_plain_data += 2;
            /*     decode ack         */
            memcpy((uint8_t *) & (protocol.ack), p_plain_data, sizeof(protocol.ack));
            p_plain_data += 2;

            if (protocol.cmd != CMD_F1)
            {
                log_debug("receive seq:%02x, cmd:%02x, expect seq:%02x\r\n", protocol.seq, protocol.cmd, expect_seq);
                //F1指令不比较，比较非F1指令
                //收到疑似非法重放数据包，返回错误。会清空rcv_buf，重新建立连接
//                if (protocol.seq != expect_seq)
//                {
//                    log_error("receive wrong seq:%02x, cmd:%02x, expect seq:%02x\r\n", protocol.seq, protocol.cmd, expect_seq);
//                    return ERR_NOT_EXPECT_SEQ;
//                }
            }
            expect_seq = protocol.seq + 1;

            _recv_seq = protocol.seq;

            client->cmd_map[cmd_index].decode_cb(charger, p_plain_data, plain_data_len - 4);

            if (client->cmd_map[cmd_index].cmd_type == CMD_TYPE_UP_RESP || \
                client->cmd_map[cmd_index].cmd_type == CMD_TYPE_UP_RESP_OR_DOWN)
            {
                item = net_queue_find(&client->snd_queue, protocol.cmd, protocol.ack);
                //找不到对应的请求包，返回错误，会清空rcv_buf，重新建立连接
                if (client->cmd_map[cmd_index].cmd_type == CMD_TYPE_UP_RESP && item == NULL)
                {
                    log_error("receive cmd:%02x, ack:%02x, not find in send queue\r\n", protocol.cmd, protocol.ack);
                    return ERR_ACK_OF_UNKNOWN;
                }
                //账单发送成功，删除持久化存储的账单
                if (CMD_61 == protocol.cmd)
                {
                    //11:实际data偏移地址，13:实际data长度
                    bill_index = bill_compare(client, item->start + 11, item->size - 13);
                    bill_delete(bill_index);
                }

                if (item != NULL)
                {
                    item->state = PROTO_QUEUED_COMPLETE;
                }
            }
release_buf:
            dest = client->rcv_buf.start;
            src  = client->rcv_buf.start + ret;
            n = client->rcv_buf.curr - client->rcv_buf.start - ret;
            memmove(dest, src, n);
            client->rcv_buf.curr -= ret;
            client->rcv_buf.free_len += ret;
        }
    }
}

static int32_t net_send(net_client_t *client)
{
    net_queue_item_t *q_item = NULL;
    int32_t item_cnt = 0, ret = ERR_SUCCESS, i = 0, need_send = 0;

    if (OS_MUTEX_LOCK(&client->snd_data_mutex, QUENU_MUTEX_TIME) != 0)
    {
        log_error("net_send OS_MUTEX_LOCK error\r\n");
        return ret;
    }

    item_cnt = net_queue_item_cnt(&client->snd_queue);
    if (item_cnt == 0)
    {
        OS_MUTEX_UNLOCK(&client->snd_data_mutex);
        return ret;
    }

    for (i = 0; i < item_cnt; i++)
    {
        q_item = net_queue_get_item(&client->snd_queue, i);
        need_send = 0;
        if (q_item->state == PROTO_QUEUED_UNSENT)  //the first time to send
        {
            need_send = 1;
        }
        else if (q_item->state == PROTO_QUEUED_AWAITING_ACK)
        {
            if (os_get_time() > q_item->time_sent + CMD_RESPONSE_TIMEOUT)
            {
                if (q_item->cmd == CMD_F0)
                {
                    OS_MUTEX_UNLOCK(&client->snd_data_mutex);
                    log_error("try to access timeout!\r\n");
                    return -1;
                }
                //timeout need to be resend
                if (q_item->send_times++ == CMD_RESEND_TIMES)
                {
                    OS_MUTEX_UNLOCK(&client->snd_data_mutex);
                    log_error("resend [%02x] more than %d times!\r\n", q_item->cmd, CMD_RESEND_TIMES);
                    return -1;
                }
                else
                {
                    need_send = 1;
                    log_debug("resend [%02x] %d times!\r\n", q_item->cmd, q_item->send_times);
                }
            }
        }

        if (!need_send)
        {
            continue;
        }

        ret = _tcp_write(client->sock_fd, (const int8_t *)q_item->start, q_item->size, 10);
        if (ret < 0)
        {
            OS_MUTEX_UNLOCK(&client->snd_data_mutex);
            return ret;
        }
        else if (ret == 0)
        {
            /* do nothing */
        }
        else
        {
            q_item->time_sent = os_get_time();
            ret = proto_get_send_cmd_index(q_item->cmd);
            if (ret < 0)
            {
                OS_MUTEX_UNLOCK(&client->snd_data_mutex);
                return ret;
            }

            switch (client->cmd_map[ret].cmd_type)
            {
            case CMD_TYPE_UP :
            case CMD_TYPE_DOWN_RESP:
                q_item->state = PROTO_QUEUED_COMPLETE;
                break;
            case CMD_TYPE_UP_RESP:
            case CMD_TYPE_UP_RESP_OR_DOWN:
                q_item->state = PROTO_QUEUED_AWAITING_ACK;
                break;
            default:
                log_error("send cmd type[%d] err\r\n", client->cmd_map[ret].cmd_type);
                OS_MUTEX_UNLOCK(&client->snd_data_mutex);
                return -1;
            }
        }
    }

    net_queue_clean_sent(&client->snd_queue);

    OS_MUTEX_UNLOCK(&client->snd_data_mutex);

    return ERR_SUCCESS;
}

void net_diss_handle(charger_t *charger)
{
    net_queue_item_t *q_item = NULL;
    int32_t item_cnt = 0, i = 0;
    int8_t bill_index = 0;
    uint8_t gun_id = 0;
    net_client_t *client = &charger->net_client;

    item_cnt = net_queue_item_cnt(&client->snd_queue);
    for (i = 0; i < item_cnt; i++)  //遍历队列，查看是否有未发送成功的账单或刷卡命令
    {
        q_item = net_queue_get_item(&client->snd_queue, i);
        if (q_item->state == PROTO_QUEUED_AWAITING_ACK)
        {
            if (q_item->cmd == CMD_60)  //如果有未发送的账单，则保存至本地ROM
            {
                log_debug("found bill in send queue\r\n");
                bill_index = bill_compare(client, q_item->start + 9, (q_item->start[8] << 8) | q_item->start[7]);
                bill_save(bill_index);
            }
#if SUPPORT_OFFLINE_CARD
            else if (q_item->cmd == CMD_22)  //如果有未发送的刷卡启动，则直接进行离线刷卡流程
            {
                gun_id = proto_get_gun_id_by_pack(client, q_item->start + 9, (q_item->start[8] << 8) | q_item->start[7]);
                log_debug("found card start cmd in send queue, gun_id:%d\r\n", gun_id);
                if (gun_id != 0)
                {
                    event_add_common(&charger->event, EVENT_TYPE_CHARGE_START, gun_id, NULL, 0);
                }
            }
#endif
        }
    }

    charger->ota_info.ota_state = OTA_DEFAULT;

    event_del_all_except_bill(&charger->event);
    net_queue_clean_all(&client->snd_queue);
    net_rcv_buf_clean_all(&client->rcv_buf);
    user_cb_dissconnected(os_get_time());
}

int32_t net_sync(charger_t *charger)
{
    int32_t err = ERR_SUCCESS;

    err = net_recv(charger);
    if (err != ERR_SUCCESS)
    {
        return err;
    }

    err = net_send(&charger->net_client);
    if (err != ERR_SUCCESS)
    {
        return err;
    }

    return err;
}

void net_client_init(net_client_t *client)
{
    memset(client, 0, sizeof(net_client_t));

    net_queue_init(&client->snd_queue, client->sbuf, SEND_BUF_SIZE);

    OS_MUTEX_INIT(&client->snd_data_mutex);

    client->rcv_buf.start = client->rbuf;
    client->rcv_buf.size = RECV_BUF_SIZE;
    client->rcv_buf.curr = client->rcv_buf.start;
    client->rcv_buf.free_len = client->rcv_buf.size;

    client->cmd_map = _proto_cmd_map;

    client->time_of_last_recv = 0;

#if SUPPORT_CRYPT
    client->encrypt = aes_cbc128_encrypt;
    client->decrypt = aes_cbc128_decrypt;
    memset(client->crypt_buf, 0, sizeof(client->crypt_buf));
#endif
    client->net_status = NET_STATUS_TRY_CONNECT;
}

int32_t proto_try_enqueue_cmd(net_client_t *client, uint8_t cmd, void *param)
{
    int32_t plain_data_len = 0;
    int8_t ci = 0;
    int8_t bill_index = 0;

    ci = proto_get_send_cmd_index(cmd);
    if (ci < 0)
    {
        return ci;
    }

    if (NET_STATUS_ACCESS_ALLOW != client->net_status && CMD_F0 != cmd)
    {
        if (CMD_60 == cmd)
        {
            log_debug("network error, save bill to rom\r\n");
            plain_data_len = client->cmd_map[ci].encode_cb(param, client->cmd_buf, sizeof(client->cmd_buf));
            bill_index = bill_update(client->cmd_buf, plain_data_len);
            bill_save(bill_index);
        }
        return ERR_PROTOCOL_OFFLINE;
    }
    plain_data_len = client->cmd_map[ci].encode_cb(param, client->cmd_buf, sizeof(client->cmd_buf));

    if (CMD_60 == cmd)
    {
        bill_update(client->cmd_buf, plain_data_len);
    }

    if (OS_MUTEX_LOCK(&client->snd_data_mutex, QUENU_MUTEX_TIME) != 0)
    {
        log_error("proto_try_enqueue_cmd OS_MUTEX_LOCK error\r\n");
        return -1;
    }

    proto_encode_pack(client, client->cmd_buf, plain_data_len, cmd);
    OS_MUTEX_UNLOCK(&client->snd_data_mutex);

    return 0;
}
/***********************************************************************************************/
